package com.huawang.mdesign.model.service.util;

import com.huawang.business.constants.ErrorCode;
import com.huawang.business.exception.AbortExecutionException;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author admin
 */
public class ReflectionUtil {

    public static final String CLASS = ".class";

    private ReflectionUtil() {
    }

    @SuppressWarnings({"unchecked"})
    public static <T> List<T> getSubclass(@NotNull Class<T> interfaceClass, @Nullable String packageName) {
        if (StringUtils.isBlank(packageName)) {
            packageName = interfaceClass.getName();
            packageName = packageName.substring(0, packageName.lastIndexOf("."));
        }

        return getClassesForPackage(packageName)
                .stream()
                .filter(e -> interfaceClass.isAssignableFrom(e) && !e.equals(interfaceClass))
                .map(e -> {
                    try {
                        return (T) e.newInstance();
                    } catch (InstantiationException | IllegalAccessException ex) {
                        throw AbortExecutionException.build(ErrorCode.PARAM_ERROR, ex);
                    }
                })
                .collect(Collectors.toList());
    }

    @SneakyThrows
    private static List<Class<?>> getClassesForPackage(String packageName) {
        List<Class<?>> classes = new ArrayList<>();
        Enumeration<URL> resources = Thread.currentThread()
                .getContextClassLoader()
                .getResources(packageName.replace('.', '/'));

        while (resources.hasMoreElements()) {
            File file = new File(resources.nextElement().getFile());
            if (file.isDirectory()) {
                classes.addAll(findClasses(file, packageName));
            }
        }
        return classes;
    }

    @SneakyThrows
    private static List<Class<?>> findClasses(File directory, String packageName) {
        File[] files = directory.listFiles();
        if (Objects.isNull(files)) {
            return Collections.emptyList();
        }

        List<Class<?>> classes = new ArrayList<>();
        for (File file : files) {
            if (file.isDirectory()) {
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(CLASS)) {
                classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - CLASS.length())));
            }
        }
        return classes;
    }
}
